#include <cstring>
#include "../config.h"
#include "client.h"
#include "ui_cli.h"
#include "proto.h"

#include "proto_cddb.h"

// ---------------------------------------------
inline void outputHelp()
// ---------------------------------------------
{
  std::cout << "Usage: " << PACKAGE << " [ <ui> [ <lvl> ] ]" << std::endl;
  std::cout << " Availabe <ui>s:" << std::endl;
  std::cout << "   cli - command line interface" << std::endl;
  std::cout << " Message output <lvl>:" << std::endl;
  std::cout << "     n - notice level output" << std::endl;
  std::cout << "     v - verbose level output" << std::endl;
  std::cout << "     d - debug level output" << std::endl;
}

// ---------------------------------------------
XmmsCdinfo::XmmsCdinfo() : client(PACKAGE) {}
int XmmsCdinfo::main(int argc, char** argv)
// ---------------------------------------------
{
	std::cout << PACKAGE_STRING << ", 2009-2012 Tomasz Sobczyk" << std::endl << std::endl;

  std::string ui_arg;
  std::string msgarg;

  switch (argc)
  {
  case 3:
    msgarg = argv[2];
  case 2:
    ui_arg = argv[1];
  case 1:
    if (ui_arg.empty()) ui_arg = "cli";
    if (msgarg.empty()) msgarg = "n";
    break;
  default:
    outputHelp();
    return EXIT_FAILURE;
  }

  if (ui_arg.compare("cli") == 0)
  {
    ui = new UiCli;
  }
  else
  {
    outputHelp();
    return EXIT_FAILURE;
  }

  if (msgarg.compare("n") == 0)
  {
    ui->setMsgLevel(Ui::NOTICE);
  }
  else
  if (msgarg.compare("v") == 0)
  {
    ui->setMsgLevel(Ui::VERBOSE);
  }
  else
  if (msgarg.compare("d") == 0)
  {
    ui->setMsgLevel(Ui::DEBUG);
  }
  else
  {
    outputHelp();
    return EXIT_FAILURE;
  }
  
	// register protocols
	//  This is made so that protocols could be
	//  moved out to shared objects
	Proto* cddb_proto = new ProtoCddb;
	registerProto(cddb_proto);

	// read the configuration file
	if (!cfg.load())
	{
		ui->msg(Ui::NOTICE, "XmmsCdinfo") << "Loading default configuration..." << Ui::END;
		setDefaultCfg();
	}

	// try to connect to the server at startup
	if (!xmmsConnect())
	{
		return EXIT_FAILURE;
	}

	// enter the interactive application loop
	ui->mainloop();

	return EXIT_SUCCESS;
}

// ---------------------------------------------
XmmsCdinfo::~XmmsCdinfo()
// ---------------------------------------------
{
	delete ui;
	std::list<Proto*>::iterator i;
	for (i = registry.begin(); i != registry.end(); i++)
	{
		delete *i;
	}
}

// ---------------------------------------------
void XmmsCdinfo::setDefaultCfg()
// ---------------------------------------------
{
	nvpairs_t config;
	std::list<std::string> vals;
	vals.push_back("cddb://freedb.freedb.org:8880");
	config.insert( std::make_pair("source", vals) );
	cfg.setPairs("", config);
}

// ---------------------------------------------
Ui* const XmmsCdinfo::getUI()
// ---------------------------------------------
{
	return ui;
}

// ---------------------------------------------
void XmmsCdinfo::registerProto(Proto* p)
// ---------------------------------------------
{
	registry.push_back(p);
	ui->msg(Ui::DEBUG, "XmmsCdinfo") << "Registered new protocol '" << p->name << "'." << Ui::END;
}

// ---------------------------------------------
void XmmsCdinfo::unregisterProto(Proto* p)
// ---------------------------------------------
{
	std::list<Proto*>::iterator i;
	for (i = registry.begin(); i != registry.end(); i++)
	{
		if ((*i) == p)
		{
			registry.erase(i);
			ui->msg(Ui::DEBUG, "XmmsCdinfo") << "Unregistered protocol '" << (*i)->name << "'." << Ui::END;
			break;
		}
	}	
}

// ---------------------------------------------
bool XmmsCdinfo::xmmsConnect()
// ---------------------------------------------
{
	try
	{	
		// connect to the server
		client.connect( std::getenv("XMMS_PATH") );

		if (client.isConnected())
		{
			ui->msg(Ui::NOTICE, "XmmsCdinfo") << "Connected to the XMMS2 server." << Ui::END;
		}

		// set the CD device used by the XMMS2 server
		cddev = client.config.valueGet("cdda.device");
		ui->msg(Ui::DEBUG, "XmmsCdinfo") << "CD device set to: " << cddev << Ui::END;
		return true;
	}	
	catch (Xmms::connection_error& err)
	{
		ui->msg(Ui::ERROR, "XmmsCdinfo") << "Connection failed: " << err.what() << Ui::END;
		return false;
	}
}

// ---------------------------------------------
bool XmmsCdinfo::xmmsUpdate(const disc_t& disc)
// ---------------------------------------------
{
	// reconnect to XMMS2 if connection not open
	if (!client.isConnected() && !xmmsConnect())
	{
		return false;
	}
	try
	{
		int trk;
		char mlib_uri[256];
		int id;
		for (trk=0; trk<disc.tracks.size(); trk++)
		{
			memset(mlib_uri, 0, 256);
			sprintf(mlib_uri, "cdda://%s/%d", disc.disc_id.data(), trk+1);
			id = client.medialib.getID(std::string(mlib_uri));
			client.medialib.entryPropertySet(id, "title", disc.tracks[trk].title, PACKAGE);
			client.medialib.entryPropertySet(id, "artist", disc.tracks[trk].artist, PACKAGE);
			client.medialib.entryPropertySet(id, "album", disc.title, PACKAGE);
			client.medialib.entryPropertySet(id, "date", disc.year, PACKAGE);
			if (!disc.tracks[trk].comment.empty())
			{
				client.medialib.entryPropertySet(id, "comment", disc.tracks[trk].comment, PACKAGE);
			}
			else
			{
				client.medialib.entryPropertySet(id, "comment", disc.comment, PACKAGE);
			}
			ui->msg(Ui::VERBOSE, "XmmsCdinfo") << "Updated data for " << mlib_uri << Ui::END;
		}
	}
	catch (std::exception& err)
	{
		ui->msg(Ui::ERROR, "XmmsCdinfo") << "Exception caught: " << err.what() << Ui::END;
		return false;
	}
	return true;
}

// ---------------------------------------------
bool XmmsCdinfo::getDiscInfo(disc_t& disc)
// ---------------------------------------------
{
	// get disc information using libdiscid
	DiscId *discid = discid_new();
	if ( discid_read(discid, cddev.data()) == 0 )
	{
		ui->msg(Ui::ERROR, "XmmsCdinfo") << "libdiscid: " << discid_get_error_msg(discid) << Ui::END;
		return false;
	}

	disc.disc_id = discid_get_id(discid);
	disc.cddb_id = discid_get_freedb_id(discid);

	int ftrack = discid_get_first_track_num(discid);
	int ltrack = discid_get_last_track_num(discid);
	disc.length = discid_get_sectors(discid) / 75; // 75 sectors/sec

	disc.artist.clear();
	disc.title.clear();
	disc.year.clear();
	disc.genre.clear();
	disc.comment.clear();
	disc.tracks.clear();

	track_t track;
	for (int i=0; i<ltrack-ftrack+1; ++i)
	{
		track.offset = discid_get_track_offset(discid, ftrack+i);
		disc.tracks.push_back(track);
	}
	discid_free(discid);

	ui->msg(Ui::VERBOSE, "XmmsCdinfo") << "Disc ID: " << disc.disc_id << Ui::END;
	ui->msg(Ui::VERBOSE, "XmmsCdinfo") << "CDDB ID: " << disc.cddb_id << Ui::END;
	ui->msg(Ui::VERBOSE, "XmmsCdinfo") << "Tracks: " << disc.tracks.size() << Ui::END;

	// get global config
	nvpairs_t config = cfg.getPairs("");

	if (config.count("source") == 0)
	{
		ui->msg(Ui::NOTICE, "XmmsCdinfo") << "No sources have been set. Cannot look up." << Ui::END;
		return false;
	}

	// iterate through the source variables
	std::list<std::string>::iterator source;
	std::list<Proto*>::iterator proto;
	uri_t uri;

	for (source = config["source"].begin(); source != config["source"].end(); source++)
	{
		uri = Cfg::parseUri( *source );
		ui->msg(Ui::VERBOSE, "XmmsCdinfo") << "Trying URI: " << (*source) << Ui::END;
		for (proto = registry.begin(); proto != registry.end(); proto++)
		{
			ui->msg(Ui::DEBUG, "XmmsCdinfo") << "Protocol set to '" << (*proto)->name << "'." << Ui::END;
			if ((*proto)->checkScheme(uri.scheme))
			{
				ui->msg(Ui::VERBOSE, "XmmsCdinfo") << "Using protocol '" << (*proto)->name << "'." << Ui::END;
				if (! (*proto)->fillDiscData(uri, disc))
				{
					ui->msg(Ui::NOTICE, "XmmsCdinfo") << "Lookup failed. :(" << Ui::END;
					return false;
				}
				break;
			}
		}
	}
	
	return true;
}
